home *** CD-ROM | disk | FTP | other *** search
-
- $Id: CodingStandards,v 1.2 1996/07/13 20:51:24 heinz Exp $
-
- Some rules programmers should adhere to
- =======================================
-
- The zeroeth law of C programming:
- ---------------------------------
-
- 0. Fully agree on the coding rules BEFORE you start the project and make
- them available IN WRITING. Revise them as necessary in a group effort
- and document reasons for changing them.
-
-
- First a few comments about looks and braces:
- --------------------------------------------
-
- - BE CONSISTENT. No matter what coding style you choose, use it
- consistently. READABILITY COUNTS.
-
- - BE CAREFUL ABOUT TABS IN YOUR SOURCES. Tabs can be the most
- obnoxious things in the world. They can even shut down your work
- or at least waste valuable resources. Why? Because nobody cares
- about the details. Due to historical reasons the tab character
- should represent eight spaces. To be exact it should represent a
- move to the next column with its number being a multiple of eight.
- This value of eight spaces is very important as it is THE ONLY
- value where portability including all alignment of the sources
- between different OS or editing environments can be guaranteed.
-
- It is common practice to use tabs to indent sources. That is most
- definitely not a bad thing. Unfortunately most brain damaged
- editing environments allow arbitrary configuration of the tab
- width for the display and then save these "display tabs" as "disk
- tabs". This messes up all alignment once the next team member
- loads the source into his favourite editing environment. This can
- decrease readability to almost zero, and it is guaranteed that
- people will get into a "formatting war" instead of doing the job
- necessary. Obviously this is a totally unacceptable situation.
- There is only one solution:
-
- Configure your editing environment to differentiate between
- "disk tabs" of eight spaces and "screen tabs". You work with
- screen tabs and your environment will have to automatically
- convert between your favourite screen tab setting to the disk
- tab size of 8 whenever it writes documents to disk. Of course
- on loading an appropriate conversion to screen tabs has to take
- place, too. If the environment does not support something like
- this, trash it or don't use tabs in any source at all.
- Otherwise you WILL have a problem.
-
- Concerning screen tab expansion, I strongly recommend a setting
- of four. This allows for easy and efficient conversion to disk
- tabs and gives a visually effective indent in pretty much any
- programming language.
-
- Be extremely carful about automatic tab conversions in your
- editing environment. If there is any chance that other members of
- your team have to access the sources, automatic tab conversion
- should be disabled. Otherwise the usefulness of any revision
- control system in development is severly limited as the sources
- will change "randomly" between certain tabs and space
- configurations.
-
- - MARK THE CLOSING BRACES WITH COMMENTS! Once you have seen >10 "END" or
- "}" statements in a row and spent >10 minutes to figure out where they
- belong, you know what I mean. Never just write "}" and nothing
- more, write "} /* if */" or "} /* function_foo */". This is
- important. Modern editing environments can be configured to do
- this automatically for you.
-
- - Having matching braces (as opposed to the K&R style) aligned in one
- column makes finding the matching brace a lot easier. The brain
- gets 87% of its outside information via the eyes. Simple
- improvements in readability like this will save time.
-
- Example of a coding style concentrating on readability:
-
- void function(int foo, int bar)
- {
- switch(foo)
- {
- case 1:
- if(bar == 2)
- {
- /* Do something about it! */
- } /* if */
- break;
- default:
- break;
- } /* switch */
-
- } /* function */
-
- Naming a closing brace (or an #endif statement) as described
- above helps matching them even further, especially when moving
- through code of a team member.
-
- - ALWAYS USE BRACES, even for single statements. This helps avoiding errors
- when code is revised and statements are added.
-
- if(bla)
- foo = test(foo);
- else
- foo = test2(foo);
-
- is MUCH more error prone than:
-
- if(bla)
- {
- foo = test(foo);
- }
- else
- {
- foo = test2(foo);
- } /* if */
-
- With braces, changing code is less dangerous. Without braces it
- can be very easy to get lost and to overlook a statement
- dependency when you have to work with code of some other team
- member. Safety counts and helps to avoid delays and expensive bug
- fix releases "just because someone overlooked a semicolon" or
- because of a similar stupid reason.
-
- - DO NOT FAKE OTHER LANGUAGES, e.g. PASCAL BEHAVIOUR. Redefining e.g.
- "{"/"/" as "BEGIN"/"END" with the preprocessor is IMHO a misuse of the
- language. To the experienced it is as misleading as to beginners, and non
- standard constructs reduce readability because "nobody" knows about them.
- Using the language as it was defined allows your team to stay with
- standard references and manuals. If someone does not _want_ to use a
- language as is and _insists_ on aliasing its constructs to his favourite
- thing, he/she'd be better in some other place. This person will probably
- misuse any language. Using a screwdriver as a hammer does not lead to
- results.
-
- - LIMIT FUNCTION SIZE. Once the text of a function is much more than e.g.
- two screen sizes, split it up into subfunctions. Huge functions make
- revisions and maintenance a pain. Typically, performance isn't an
- issue here and link time is less than compile time.
-
- - LIMIT FILE SIZE. A C source file should never be longer than a
- few hundred lines. 1k lines is the absolute upper limit.
- Modularity helps a lot with C, too. Consider the linking process
- to be a feature.
-
- - USE HEADER FILES. Create prototype header files and header files with the
- externally needed data declarations. Use them as help for making black
- box modules.
-
- - Separate functions with some sort of header or even a simple line:
-
- /*------------------------------------------------------------------------*/
-
- This makes fast reading of source files and understanding their contents
- a lot easier. Don't use "bold" lines, e.g. lines made up of
- asterisks '*'. Depending on the display they lose their line-like
- character and clutter the screen instead of helping to make
- things readable.
-
- - Use a standard header and footer for each source file. If you are using
- a Revision control system, the header should contain an identification.
- This header can be quite simple:
-
- /*------------------------------------------------------------------------*/
- /* *
- * $Id: CodingStandards,v 1.2 1996/07/13 20:51:24 heinz Exp $
- * */
- /*------------------------------------------------------------------------*/
-
- /*------------------------------------------------------------------------*/
- [headers here]
-
- /*------------------------------------------------------------------------*/
- [code here]
-
- /*------------------------------------------------------------------------*/
-
- /* End of Text */
-
-
-
-
- Now some comments on use of the language and compiler:
- ------------------------------------------------------
-
- - USE THE ANSI C STANDARD for declarations. Avoid K&R wherever possible.
- The compiler is your friend and will do better error checking
- with ANSI prototypes.
-
- - ALWAYS RUN the compiler system with THE STRICTEST ERROR CHECKING MODE
- possible. C compilers have limits to their diagnostic ability, so use what
- is available. ERRORS AND WARNINGS ARE NOT ACCEPTABLE!
-
- - READ THE "IMPLEMENTATION-DEFINED BEHAVIOUR" SECTION OF THE MANUAL BEFORE
- YOU START. While there is a standard for C, there are still things that
- can bite you if you are not aware of them. ANSI C does not
- automatically mean "great, portable, and future compatible".
-
- - HAVE THE LANGUAGE DEFINITION IN REACH FOR ALL TEAM MEMBERS. I also
- recommend having a book like "P.J.Plaugher: The Standard C Library"
- around as reference to everyone on the team. IF IN DOUBT, LOOK IT UP! IT
- IS NOT A SHAMEFUL THING TO DO!
-
- - STAY WITH STANDARDS WHEREVER POSSIBLE. If you write a standard
- C application use the standard ANSI library. If you write
- something specific to a certain OS, use the tools the OS
- provides. Avoid arbitrary mixtures of standard C and OS specific
- C. Avoid use of functions specific to your current C compiler
- system or specific to the HW if you want to have protable code.
- You might need to change both eventually. This is mostly a
- problem for IBM clone programmers who think that "Borland C++" or
- "MS C++" and clones is the only thing there is in the world.
- Watch your team. Check what your compiler does per default with
- newlines or character sets! "\r\n" vs. "\n" problems can be
- eventually as painful as a compiler system that mangles ISO
- characters into 7 bit in strange places. Note that handling
- binary file data is different than text data with the ANSI C
- library. Do not rely on it being the same for all compiler
- systems.
-
- - ISOLATE NON PORTABLE CODE.
-
- - USE "const" and "volatile". Especially "const" helps the compiler and the
- poor soul who writes and debugs the code a lot. Wise use of "const"
- enhances the compiler error checking capabilities usually a lot.
- Some compilers may generate poor code with these keywords
- unfrtunately. In that case an easy solution is to define them to
- nothing when generating production code.
-
- - Define anything that does not need to be externally visible as "static".
- LIMITING SCOPE of unneeded stuff helps a) the linker, b) modularity, c) a
- strong black box approach for modules.
-
- - USE THE PREPROCESSOR CAREFULLY. While it may be useful to give strange
- constants nice names, use of it should be monitored. Major caveat: C is a
- language where expressions often have side effects. Side effects hidden
- in preprocessor macros are not diagnosed by all compiler systems! Do not
- overdo it here!
-
- - If a source file has self contained functionality like e.g time
- conversion stuff, think hard about ADDING A TEST CASE FOR CONDITIONAL
- COMPILE in the first place.
-
- #ifdef TEST
- int main(int argc, const char **argv)
- {
- /* test the darn thing here! */
- } /* main */
- #endif /* TEST */
-
- - REMEMBER: THE LINKER IS YOUR FRIEND!
-
- - If your compiler system has an automatic build facility like "make", use
- it. "make" is a very helpful tool. If you have some sort of custom
- integrated compiler environment, make sure that you can comparatively
- easy fall back on a standard make like scheme. Be careful about
- all the different incarnations of "make". KEEP THE PARACHUTE
- CLOSE.
-
- - C IS USUALLY MOST EFFICIENT WHEN USED WITH POINTERS. Passing data
- structures is not good, most things are done with pointers. If your team
- does not have a FIRM UNDERSTANDING OF POINTERS, teach them first. Don't
- let them fiddle along. They need to know first or you might be in deep
- trouble.
-
- - CHECK FUNCTION RESULTS FOR ANY FUNCTION THAT MAY FAIL. A common error is
- not checking for failure on e.g. memory allocations. ASSUME A NEGATIVE
- SITUATION when writing the code. It will hardly ever be a performance hit
- if you do and increase reliability.
-
-
- Comments about debugging C:
- ---------------------------
-
- - CHECK FOR NULL POINTERS AND POINTER OVERRUNS.
-
- - C STRINGS HAVE A TRAILING NULL BYTE AND ARE THEREFORE ONE BYTE LONGER
- THAN THE CONTENTS!
-
- - CHECK FOR MISSING "*/" END OF COMMENTS. YOU MIGHT HAVE LOST A LINE OF
- CODE.
-
- - If a bug is still hiding and can't be found, have the author explain the
- functionality of the code in question line by line including limit and
- exception handling to someone on the team who is not directly involved in
- this part of the project. This will usually solve the problem pretty
- fast and save additional cost.
-
-
- And now something short on code management:
- -------------------------------------------
-
- - USE some sort of A REVISION CONTROL SYSTEM like RCS, CVS, SCCS ...
- PUT A FILE ID COMMENT (like the RCS 'Id' keyword) INTO EACH
- SOURCE FILE. Otherwise identifying a printed revision is pretty
- much impossible once you get swamped in paper. I use RCS. Do not
- ever use expanding 'Log' like keywords in your sources. They just
- duplicate what is in the repository already and they'll make
- identifying differences even harder. After a while they also
- clutter the source.
-
- - Mark stable revisions and flame everyone who makes too many changes to
- the code before using the revision control system to check it in again.
- There should not be any work files not being checked in lying around at
- the end of a working day!
-
-
- last but not least: Have fun.
-
- Heinz Wrobel
- Karlstr. 16
- 82131 Gauting
- FAX +49 89 850 5125
- <heinz@hwg.muc.de>
-
-